import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
*
* Ori Weisler  ID 060512878
* Rony Lahav   ID 043489889 
* Date: Jun 24, 2009
*/

public class Interpreter {

	private static BufferedReader prog;	//for the JVM program content
	private static BufferedReader vars;	//for the variables file content
	private static BufferedWriter out;
	//constant Strings
	private static final String JVM_FILE_NAME = "inputJVM.txt";
	private static final String VARS_FILE_NAME = "input.txt";
	public static final String COMMAND_RD = "RD";
	public static final String COMMAND_WRD = "WRD";
	public static final String COMMAND_ADD = "ADD";
	public static final String COMMAND_ADDI = "ADDI";
	public static final String COMMAND_SUB = "SUB";
	public static final String COMMAND_SUBI = "SUBI";
	public static final String COMMAND_MUL = "MUL";
	public static final String COMMAND_MULI = "MULI";
	public static final String COMMAND_DIV = "DIV";
	public static final String COMMAND_DIVI = "DIVI";
	public static final String COMMAND_LDW = "LDW";
	public static final String COMMAND_STW = "STW";
	
	//executes the RD command
	private static boolean execRD(int readReg, int var){
		//Number 50: store var into readReg
		try{
			Compiler.registers[readReg]=var;
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the WRD command
	private static boolean execWRD(int writeReg) throws IOException{
		//Number 51: write from writeReg into System.out
		System.out.println(Compiler.registers[writeReg]);
		out.write(Integer.toString(Compiler.registers[writeReg]));
		out.newLine();
		return false;
	}
	
	//executes the AAD command
	private static boolean execADD(int destinationReg, int num1Reg, int num2Reg){
		//Number 0: num1+num2 -> destinationReg
		try {
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]+Compiler.registers[num2Reg];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the ADDI command
	private static boolean execADDI(int destinationReg, int num1Reg, int num2){
		//Number 16: num2 is an immediate
		//num1+num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]+num2;
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the SUB command
	private static boolean execSUB(int destinationReg, int num1Reg, int num2Reg){
		//Number 1: num1-num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]-Compiler.registers[num2Reg];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the SUBI command
	private static boolean execSUBI(int destinationReg, int num1Reg, int num2){
		//Number 17:  num2 is an immediate
		//num1-num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]-num2;
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the MUL command
	private static boolean execMUL(int destinationReg, int num1Reg, int num2Reg){
		//Number 2: num1*num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]*Compiler.registers[num2Reg];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the MULI command
	private static boolean execMULI(int destinationReg, int num1Reg, int num2){
		//Number 18: num2 is an immediate
		//num1*num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]*num2;
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the DIV command
	private static boolean execDIV(int destinationReg, int num1Reg, int num2Reg){
		//Number 3: num1/num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]/Compiler.registers[num2Reg];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the DIVI command
	private static boolean execDIVI(int destinationReg, int num1Reg, int num2){
		//Number 19: num2 is an immediate
		//num1/num2 -> destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.registers[num1Reg]/num2;
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the LDW command
	private static boolean execLDW(int destinationReg, int memReg, int constShift){
		//Number 32: load memory[memReg+constShift] into destinationReg
		try{
		    Compiler.registers[destinationReg]=Compiler.memory[(Compiler.registers[memReg]+constShift)/4];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//executes the STW command
	private static boolean execSTW(int originReg, int memReg, int constShift){
		//Number 36: store originReg into memory[memReg+constShift]
		try{
		    Compiler.memory[(Compiler.registers[memReg]+constShift)/4]=Compiler.registers[originReg];
		} catch (Exception e) {
			return true;
		}
		return false;
	}
	
	//If the command was received as a number returns it,
	//else returns the corresponding number for each command
	private static int getCommandNumber(String command){
		
		try {
			int com = Integer.parseInt(command);	//command received as a number
			return com;
		} catch (Exception e) {	//command is "verbal"
			if (command.equals(COMMAND_ADD))
				return 0;
			if (command.equals(COMMAND_ADDI))
				return 16;
			if (command.equals(COMMAND_SUB))
				return 1;
			if (command.equals(COMMAND_SUBI))
				return 17;
			if (command.equals(COMMAND_MUL))
				return 2;
			if (command.equals(COMMAND_MULI))
				return 18;
			if (command.equals(COMMAND_DIV))
				return 3;
			if (command.equals(COMMAND_DIVI))
				return 19;
			if (command.equals(COMMAND_LDW))
				return 32;
			if (command.equals(COMMAND_STW))
				return 36;
			if (command.equals(COMMAND_RD))
				return 50;
			if (command.equals(COMMAND_WRD))
				return 51;
		}
		return -1;
	}
	
	public static void executeJVMProgram() throws IOException {
		out = new BufferedWriter(new FileWriter( "output.txt"));	
		String separatedVars[] = null;
		int varIndex=0;
		try {
			prog = new BufferedReader(new FileReader(JVM_FILE_NAME));
			vars = new BufferedReader(new FileReader(VARS_FILE_NAME));
			separatedVars = vars.readLine().split(",");	//vars in inputVARS file are separated by ",'
			String line;
			line = prog.readLine();	//read first JVM command
			boolean error = false;
			do{
				//read command
				String[] readData=line.split(" ");	//for seperation by " " (space) between command and vars
				if (readData.length<2)
					readData = readData[0].split("\t");	//for seperation by "\t" (tab) between command and vars
				int command = getCommandNumber(readData[0]);	//get command number
				if (command>=0){	//if there's no error in the command
					readData = readData[1].split(",");	//vars of each command are separated by ","
					//there are always 3 vars for each command
					int var1 = Integer.parseInt(readData[0]);
					int var2 = Integer.parseInt(readData[1]);
					int var3 = Integer.parseInt(readData[2]);
					//choose which command needs to run
					switch (command) {
					
					case 0: 	//ADD
						error=execADD(var1, var2, var3);
						if (error){
							System.out.println("Error executing ADD command");
						}
						break;
						
					case 16:	//ADDI
						error=execADDI(var1, var2, var3);
						if (error){
							System.out.println("Error executing ADDI command");
						}
						break;
						
					case 1:		//SUB
						error=execSUB(var1, var2, var3);
						if (error){
							System.out.println("Error executing SUB command");
						}
						break;
						
					case 17:	//SUBI
						error=execSUBI(var1, var2, var3);
						if (error){
							System.out.println("Error executing SUBI command");
						}
						break;
						
					case 2:		//MUL
						error=execMUL(var1, var2, var3);
						if (error){
							System.out.println("Error executing MULI command");
						}
						break;
						
					case 18:	//MULI
						error=execMULI(var1, var2, var3);
						if (error){
							System.out.println("Error executing MULI command");
						}
						break;
						
					case 3:		//DIV
						error=execDIV(var1, var2, var3);
						if (error){
							System.out.println("Error executing DIVI command");
						}
						break;
						
					case 19:	//DIVI
						error=execDIVI(var1, var2, var3);
						if (error){
							System.out.println("Error executing DIVI command");
						}
						break;
						
					case 32:	//LDW
						error=execLDW(var1, var2, var3);
						if (error){
							System.out.println("Error executing LDW command");
						}
						break;
						
					case 36:	//STW
						error=execSTW(var1, var2, var3);
						if (error){
							System.out.println("Error executing STW command");
						}
						break;
						
					case 50:	//RD
						if (varIndex == separatedVars.length){	//in case of a new line in vars file
							separatedVars = vars.readLine().split(",");
							varIndex=0;
						}
						error=execRD(var3, Integer.parseInt(separatedVars[varIndex]));
						++varIndex;	//go to next var location for next RD command (if any)
						if (error){
							System.out.println("Error executing RD command");
						}
						break;
						
					case 51:	//WRD
						error=execWRD(var3);	//print result
						if (error){
							System.out.println("Error executing WRD command");
						}
						break;
						
					default:
						System.out.println("Error - wrong command name");
						break;
					}
					line = prog.readLine();	//read next JVM command
				}
				else{
					System.out.println("Error - command doesn't exist or isn't supported");
					error=true;
				}
			}while(!error && line!=null);	//error or EOF
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			prog.close();
			vars.close();
			out.close();
		}
	}

}
